Skip to content

GDB 调试信号处理程序:handle 命令与 VSCode 集成 ​

调试带信号处理逻辑的程序(SIGHUP 重载配置、SIGTERM 优雅退出、SIGUSR1 触发任务……)时有个常见障碍:GDB 默认会在信号到达时暂停程序并打印消息,正常的信号处理流程被打断,断点还没到信号先被调试器「吃掉」。本文写给在命令行或 VSCode 里用 GDB 调 C/C++ 程序的开发者,读完可以掌握 handle 命令的三个开关、info signals 的现状查询、把信号配置固化进 launch.json 的写法,以及一套完整的信号调试流程。

查看当前信号处理设置 ​

info signals(别名 info handle)列出 GDB 对所有信号的当前配置:

bash
# GDB 命令行
(gdb) info signals

# VSCode Debug Console(-exec 前缀把命令交给 GDB)
-exec info signals

输出示例:

Signal        Stop   Print   Pass to program   Description
SIGHUP        Yes    Yes     Yes               Hangup
SIGINT        Yes    Yes     No                Interrupt
SIGQUIT       Yes    Yes     Yes               Quit
...

查看单个信号用 info signal SIGHUP。三列含义对应下面 handle 的三个开关。

handle 命令:控制 GDB 对信号的行为 ​

bash
# GDB 命令行
(gdb) handle SIGHUP nostop noprint pass

# VSCode Debug Console
-exec handle SIGHUP nostop noprint pass

语法是 handle signal [signal...] keywords...,信号可按名字或编号给出,也可用 all 处理全部信号(GDB 自用的 SIGINT、SIGTRAP 除外)。关键字可以缩写:

关键字 作用 说明
stop / nostop 是否暂停 stop:收到信号时暂停程序,隐含 print;nostop:不暂停
print / noprint 是否打印 print:在控制台显示信号信息;noprint:不显示,隐含 nostop
pass / nopass 是否传递 pass:把信号传递给程序的处理函数;nopass:拦截信号
ignore / noignore 同义词 ignore 等同 nopass,noignore 等同 pass

常用组合:

bash
# 1. 调试信号处理器:不暂停、不打印、让程序正常处理
handle SIGHUP nostop noprint pass

# 2. 捕获信号观察现场:暂停并打印,但仍传递给程序
handle SIGUSR1 stop print pass

# 3. 屏蔽干扰信号(如频繁的 SIGPIPE)
handle SIGPIPE nostop noprint nopass

# 4. 恢复默认行为
handle SIGHUP stop print pass

把信号配置固化进 launch.json ​

VSCode 的 cppdbg 调试支持 setupCommands,调试会话启动时自动执行,不必每次手敲:

json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "C++ Debug with Signal Handling",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/build/bin/your_program",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "Handle SIGHUP without stopping",
                    "text": "-exec handle SIGHUP nostop noprint pass",
                    "ignoreFailures": false
                },
                {
                    "description": "Handle SIGUSR1 without stopping",
                    "text": "-exec handle SIGUSR1 nostop noprint pass",
                    "ignoreFailures": false
                },
                {
                    "description": "Ignore SIGPIPE",
                    "text": "-exec handle SIGPIPE nostop noprint nopass",
                    "ignoreFailures": false
                }
            ],
            "preLaunchTask": "build",
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}

实战:调试 SIGHUP 重载配置的完整流程 ​

示例程序——收到 SIGHUP 后重新加载配置:

cpp
#include <signal.h>
#include <unistd.h>
#include <iostream>

void signal_handler(int signum) {
    std::cout << "Received signal: " << signum << std::endl;
    // 重新加载配置...
}

int main() {
    signal(SIGHUP, signal_handler);

    while (true) {
        std::cout << "Running... (PID: " << getpid() << ")" << std::endl;
        sleep(5);
    }
    return 0;
}

调试步骤:

  1. 启动调试会话;
  2. 在 Debug Console 执行 -exec handle SIGHUP nostop print pass——不拦截信号,但保留打印以便观察;
  3. 在 signal_handler 函数内设置断点;
  4. 终端发送信号:kill -SIGHUP <pid>(PID 由程序打印);
  5. 观察结果:GDB 不会停在信号接收处,而是停在 signal_handler 断点上,可以正常单步调试处理逻辑。

触发信号的几种方式 ​

bash
# 终端按编号或名字发送
kill -SIGHUP <pid>
kill -1 <pid>
kill <pid>              # 默认 SIGTERM

# 按进程名批量发送
pkill -SIGHUP process_name

也可以让 GDB 主动发信号继续执行(恢复程序运行并当作收到该信号):

bash
(gdb) signal SIGHUP          # 命令行
-exec signal SIGHUP          # VSCode Debug Console

常用信号参考 ​

x86_64 Linux 下的常用信号:

信号 编号 默认行为 典型用途
SIGHUP 1 终止 重新加载配置
SIGINT 2 终止 Ctrl+C 中断
SIGQUIT 3 终止+Core Ctrl+\ 退出
SIGKILL 9 终止 强制杀死(不可捕获)
SIGUSR1 10 终止 用户自定义
SIGUSR2 12 终止 用户自定义
SIGTERM 15 终止 优雅终止
SIGCHLD 17 忽略 子进程状态改变

注意事项 ​

  • SIGKILL 和 SIGSTOP 无法被捕获、阻塞或忽略,handle 对它们没有意义;
  • 设置 nopass 会阻止程序的信号处理函数执行——排查「信号没到程序」类问题时先检查这里;
  • VSCode Debug Console 执行 GDB 命令必须带 -exec 前缀;
  • 直接在 Debug Console 执行命令属于未完全测试的路径,个别情况下可能不稳定,常规 handle/info 命令没有问题。

要点 ​

  • 调试信号处理逻辑的核心配置:handle <信号> nostop noprint pass——GDB 隐身,信号照常到达程序
  • 三个开关各管一件事:stop 管断不停、print 管打不打印、pass 管信号给不给程序
  • setupCommands 把配置固化进 launch.json,省去每次手敲
  • 断点放进信号处理函数,kill 从外部触发,是最顺手的信号调试组合

相关文章 ​

最近更新

基于 VitePress 构建